#ifndef _EVENTLOOP_H_
#define _EVENTLOOP_H_

#include <atomic>
#include <vector>
#include <functional>
#include <chrono>
#include <thread>
#include <queue>
namespace SEngine
{
    template<typename... Args>
    class EventLoop
    {
    public:
        enum class TaskMode {
            Immediately = 0,
            Defer = 1
        };
        using FPS = float;
        using Task = std::function<void(Args...)>;
        using TaskArgs = std::function<void(EventLoop<Args...>&, Args...)>;
    public:
        EventLoop();
        EventLoop(EventLoop && loop);
        virtual ~EventLoop();
        void Start(FPS fps, Args&&... args);
        void Stop();
        void SendTask(Task && task, TaskMode mode = TaskMode::Immediately);
        void SendTask(TaskArgs && task, TaskMode mode = TaskMode::Immediately);
        void SetContext(EventLoop && loop);
        void Start(EventLoop<Args...> & lastLoop, FPS fps, Args&&... args);


    private:
        std::atomic_bool m_isStart{false};
        std::queue<TaskArgs> m_taskQueue;
        std::queue<TaskArgs> m_taskQueueBackup;
        std::queue<TaskArgs> m_taskQueueDefer;
    };
    
    template <typename... Args>
    inline EventLoop<Args...>::EventLoop()
    {
    }

    template <typename... Args>
    inline EventLoop<Args...>::EventLoop(EventLoop &&loop)
    {  
        SetContext(std::move(loop));
    }

    template <typename... Args>
    inline EventLoop<Args...>::~EventLoop()
    {
        Stop();
    }
    
    template <typename... Args>
    inline void EventLoop<Args...>::Start(FPS fps, Args&&... args)
    {
        auto lastTime = std::chrono::steady_clock::now();
        m_isStart.store(true);
        while(m_isStart) {
            m_taskQueueBackup = std::move(m_taskQueue);
            while (!m_taskQueueBackup.empty()) {
                auto fn = m_taskQueueBackup.front();
                m_taskQueueBackup.pop();
                fn(*this, std::forward<Args>(args)...);
            }
            if (!m_taskQueueDefer.empty()) {
                auto fn = m_taskQueueDefer.front();
                m_taskQueueDefer.pop();
                fn(*this, std::forward<Args>(args)...);
            }
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - lastTime).count();
            std::this_thread::sleep_for(std::chrono::milliseconds(std::max((long long)(1000 / fps) - duration, (long long)0)));
            lastTime = std::chrono::steady_clock::now();
        }
    }
    
    template <typename... Args>
    inline void EventLoop<Args...>::Stop()
    {
        SendTask([this](EventLoop<Args...>&, Args&&... args) {
            this->m_isStart.store(false);
        });
    }

    template <typename... Args>
    inline void EventLoop<Args...>::SendTask(Task &&task, TaskMode mode)
    {
        SendTask([task](EventLoop<Args...>&, Args&&... args) {
            task(std::forward<Args>(args)...);
        }, mode);
    }

    template <typename... Args>
    inline void EventLoop<Args...>::SendTask(TaskArgs &&task, TaskMode mode)
    {
        if (mode == TaskMode::Immediately)
            m_taskQueue.emplace(std::forward<TaskArgs>(task));
        else if (mode == TaskMode::Defer)
            m_taskQueueDefer.emplace(std::forward<TaskArgs>(task));
    }

    template <typename... Args>
    inline void EventLoop<Args...>::SetContext(EventLoop &&loop)
    {
        m_taskQueue = std::move(loop.m_taskQueue);
        m_taskQueueBackup = std::move(loop.m_taskQueueBackup);
        m_taskQueueDefer = std::move(loop.m_taskQueueDefer);
    }

    template <typename... Args>
    inline void EventLoop<Args...>::Start(EventLoop<Args...> &lastLoop, FPS fps, Args &&...args)
    {
        this->SetContext(std::move(lastLoop));
        this->Start(fps, std::forward<Args>(args)...);
        lastLoop.SetContext(std::move(*this));
    }

} // namespace SEngine

#endif